package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.nursing.config.WebSocketServer;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.service.IAlertRuleService;
import com.zzyl.nursing.vo.AlertNotifyVo;
import com.zzyl.system.mapper.SysUserRoleMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 报警规则Service业务层处理
 *
 * @author mr.chen
 * @date 2025-05-29
 */
@Slf4j
@Service
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IAlertRuleService {
    @Autowired
    private AlertRuleMapper alertRuleMapper;

    /**
     * 查询报警规则
     *
     * @param id 报警规则主键
     * @return 报警规则
     */
    @Override
    public AlertRule selectAlertRuleById(Long id) {
        return getById(id);
    }

    /**
     * 查询报警规则列表
     *
     * @param alertRule 报警规则
     * @return 报警规则
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule) {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增报警规则
     *
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule) {
        return save(alertRule) ? 1 : 0;
    }

    /**
     * 修改报警规则
     *
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule) {
        return updateById(alertRule) ? 1 : 0;
    }

    /**
     * 批量删除报警规则
     *
     * @param ids 需要删除的报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids) {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除报警规则信息
     *
     * @param id 报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id) {
        return removeById(id) ? 1 : 0;
    }

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private IAlertDataService alertDataService;

    @Autowired
    private WebSocketServer webSocketServer;

    @Value("${alertRule.roleAdmin}")
    private String roleAdmin;

    @Value("${alertRule.roleRepair}")
    private String roleRepair;

    /**
     * 设备上报数据报警定时任务
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void alertFilter() {
        //查询所有规则
        Long count = this.lambdaQuery().eq(AlertRule::getStatus, 1).count();
        if (count <= 0) {
            log.warn("[定时报警规则校验]没有任何报警规则，无需处理");
            return;
        }
        //从redis查询所有设备的数据
        List<Object> values = redisTemplate.opsForHash().values(CacheConstants.HUAWEU_IOTDA_DEVICE_DATA_LIST);
        if (CollUtil.isEmpty(values)) {
            log.warn("[定时报警规则校验]没有任何设备数据，无需处理");
            return;
        }
        //数据格式化处理
        List<DeviceData> deviceDataAllList = new ArrayList<>();
        values.forEach(v -> {
            //转成实体类对象集合
            List<DeviceData> deviceDataList = JSONUtil.toList((String) v, DeviceData.class);
            //把该集合数据添加到另一个集合中
            deviceDataAllList.addAll(deviceDataList);
        });

        //遍历所有数据
        for (DeviceData deviceData : deviceDataAllList) {
            //校验设备上报时间是否太久（由业务规则决定，可变）
            int limit = 600;//单位秒
            long between = LocalDateTimeUtil.between(deviceData.getAlarmTime(), LocalDateTime.now(), ChronoUnit.SECONDS);
            if (between > limit) {
                log.warn("[定时报警规则校验]该时间超过十分钟的设备上报数据不再处理，无需处理");
                continue;
            }
            // 查询当前上报数据对应的产品类型下同样的功能绑定的全部规则数据
            List<AlertRule> alertRuleAllList = this.lambdaQuery()
                    .eq(AlertRule::getProductKey, deviceData.getProductKey())
                    .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                    .eq(AlertRule::getIotId, -1)
                    .eq(AlertRule::getStatus, 1)
                    .list();
            if (CollUtil.isEmpty(alertRuleAllList)) {
                alertRuleAllList = new ArrayList<>();
            }
            //查询当前上报数据对应的设备下绑定的规则数据
            List<AlertRule> alertRuleSingleList = this.lambdaQuery()
                    .eq(AlertRule::getProductKey, deviceData.getProductKey())
                    .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                    .eq(AlertRule::getIotId, deviceData.getIotId())
                    .eq(AlertRule::getStatus, 1)
                    .list();
            if (CollUtil.isEmpty(alertRuleSingleList)) {
                alertRuleSingleList = new ArrayList<>();
            }
            //合并所有规则数据
            alertRuleAllList.addAll(alertRuleSingleList);
            //判断是否为空
            if (CollUtil.isEmpty(alertRuleAllList)) {
                log.warn("[定时报警规则校验]没有对应数据的匹配规则，无需处理");
                continue;
            }
            //去重
            List<AlertRule> distinctAlertRuleList = CollUtil.distinct(alertRuleAllList);

            //遍历每一条规则
            for (AlertRule alertRule : distinctAlertRuleList) {
                //第一条校验规则:判断报警生效时段
                //获取上报时间
                LocalTime alarmTime = deviceData.getAlarmTime().toLocalTime();
                //获取当前规则的报警生效时段
                String alertTime = alertRule.getAlertEffectivePeriod();
                //截取字符串获取时间
                String[] splitTime = alertTime.split("~");
                //开始时间
                LocalTime startTime = LocalTime.parse(splitTime[0]);
                //结束时间
                LocalTime endTime = LocalTime.parse(splitTime[1]);
                if (alarmTime.isBefore(startTime) || alarmTime.isAfter(endTime)) {
                    log.warn("[定时报警规则校验]当前上报时间不符合当前规则的报警生效时段，无需处理");
                    continue;
                }
                //第二层校验:判断数据阈值
                //获取规则阈值
                Double value = alertRule.getValue();
                //获取设备的数据值
                Double deviceValue = Double.valueOf(deviceData.getDataValue());
                //获取运算符
                String operator = alertRule.getOperator();//比较运算符 >= 或 <
                //x大于y，结果就是1； x等于y，结果就是0； x小于y，结果就是-1
                int compare = NumberUtil.compare(deviceValue, value);
                if (">=".equals(operator) && compare >= 0 || "<".equals(operator) && compare < 0) {
                    //第三层校验：检查沉默周期是否存在，存在就直接结束
                    //编辑沉默周期的值
                    String silentKey = CharSequenceUtil.format(CacheConstants.IOT_ALERT_SILENT_PREFIX, deviceData.getIotId(), deviceData.getFunctionId(), alertRule.getId());
                    //获取沉默周期的值
                    String silentValue = redisTemplate.opsForValue().get(silentKey);
                    if (StringUtils.isNotBlank(silentValue)) {
                        log.warn("[定时报警规则校验]当前沉默周期还未结束，无需处理");
                        continue;
                    } else {
                        //第四层校验：判断持续周期对应的次数是否达到（没达到则直接结束）
                        //编辑周期次数
                        String countKey = CharSequenceUtil.format(CacheConstants.IOT_ALERT_COUNT_PREFIX, deviceData.getIotId(), deviceData.getFunctionId(), alertRule.getId());
                        //累加周期次数
                        Long countValue = redisTemplate.opsForValue().increment(countKey);
                        if (countValue < alertRule.getDuration()) {
                            log.warn("[定时报警规则校验]当前沉默周期次数不达标，无需处理");
                            continue;
                        }
                        //设置沉默周期的时长
                        redisTemplate.opsForValue().set(silentKey, "123", alertRule.getAlertSilentPeriod() * 60, TimeUnit.SECONDS);

                        //删除周期次数
                        redisTemplate.delete(countKey);

                        //老人的护理人员
                        List<Long> nursingIds = null;

                        //如果是老人异常数据，则查询对应护理人员
                        if (alertRule.getAlertDataType() == 0) {
                            //如果设备是移动设备，则直接查到老人对应的护理人员
                            if (deviceData.getLocationType() == 0) {
                                nursingIds = deviceMapper.selectNuringIdsByMoveIotId(deviceData.getIotId());
                            } else {
                                //如果设备是固定设备，则需要关联床位查询老人对应的护理人员
                                nursingIds = deviceMapper.selectNuringIdsByStopIotId(deviceData.getIotId());
                            }
                        } else {
                            //如果是设备异常数据，则查询维修工
                            nursingIds = sysUserRoleMapper.selectUserIdByRoleName(roleRepair);
                        }

                        //查询管理员
                        List<Long> roleAdminIds = sysUserRoleMapper.selectUserIdByRoleName(roleAdmin);

                        //合并数据
                        nursingIds.addAll(roleAdminIds);
                        //去除
                        List<Long> distinctNursingIds = CollUtil.distinct(nursingIds);

                        //封装数据
                        AlertData alertData = new AlertData();
                        BeanUtils.copyProperties(deviceData, alertData, "id");
                        alertData.setType(alertRule.getAlertDataType());//设备数据类型 0-老人异常数据 1-设备异常数据
                        alertData.setAlertRuleId(alertRule.getId());//规则id
                        alertData.setStatus(0);//0-待处理  1-已处理
                        String alertReason = "功能{},{},{},持续了{}个周期,沉默周期:{}";
                        String alertReasonFormat = CharSequenceUtil.format(alertReason,
                                alertRule.getFunctionId(),//功能名称
                                alertRule.getOperator(),//运算符
                                alertRule.getValue(),//阈值
                                alertRule.getDuration(),//持续周期
                                alertRule.getAlertSilentPeriod());//沉默周期
                        alertData.setAlertReason(alertReasonFormat);//报警原因
                        //所有用户报警数据的集合
                        List<AlertData> alertDataList = new ArrayList<>();
                        for (Long distinctNursingId : distinctNursingIds) {
                            AlertData userAlerData = new AlertData();
                            BeanUtils.copyProperties(alertData, userAlerData);
                            userAlerData.setUserId(distinctNursingId);
                            alertDataList.add(userAlerData);
                        }
                        //保存数据
                        boolean saveBatch = alertDataService.saveBatch(alertDataList);
                        if (!saveBatch) {
                            throw new BaseException("保存数据失败");
                        }

                        //转发给指定用户
                        AlertNotifyVo alertNotifyVo = BeanUtil.toBean(deviceData, AlertNotifyVo.class);
                        //封装数据
                        alertNotifyVo.setAccessLocation(deviceData.getRemark());//接入位置
                        alertNotifyVo.setAlertDataType(alertRule.getAlertDataType());//报警数据类型，0：老人异常数据，1：设备异常数据
                        alertNotifyVo.setFunctionName(alertRule.getFunctionName());//功能名称
                        alertNotifyVo.setNotifyType(1);//报警状态 0:解除报警 1:报警
                        alertNotifyVo.setIsAllConsumer(false);//是否全员通知
                        alertNotifyVo.setVoiceNotifyStatus(1);//报警通知类型，0：解除报警，1：报警
                        for (Long distinctNursingId : distinctNursingIds) {
                           webSocketServer.notifyMsgByUserToClient(String.valueOf(distinctNursingId),JSONUtil.toJsonStr(alertNotifyVo));
                        }

                    }
                } else {
                    log.warn("[定时报警规则校验]数据上报的值没有达到规则阈值，无需处理");
                    continue;
                }
            }
        }
    }
}
